########################################################################
#  Searx-qt - Lightweight desktop application for SearX.
#  Copyright (C) 2020  CYBERDEViL
#
#  This file is part of Searx-qt.
#
#  Searx-qt is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Searx-qt is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
########################################################################

from urllib.parse import urlparse

from PyQt5.QtWidgets import (
    QWidget,
    QFrame,
    QVBoxLayout,
    QFormLayout,
    QCheckBox,
    QLabel,
    QDoubleSpinBox,
    QLineEdit,
    QComboBox,
    QHBoxLayout,
    QSizePolicy,
    QDialog,
    QSpacerItem
)

from PyQt5.QtCore import Qt, pyqtSignal

from searxqt.widgets.groupBox import CollapsableGroupBox
from searxqt.widgets.buttons import Button

HAVE_SOCKS = False
try:
    import socks
    HAVE_SOCKS = True
except ImportError:
    print("pysocks not installed! No socks proxy support.")


class ProxyWidget(QWidget):
    changed = pyqtSignal(str)  # self.str()

    def __init__(self, parent=None):
        QWidget.__init__(self, parent=parent)
        l = QVBoxLayout(self)

        hLayout = QHBoxLayout()

        self._proxyType = QComboBox(self)
        self._proxyType.setSizePolicy(QSizePolicy(
            QSizePolicy.Maximum,
            QSizePolicy.Fixed))

        typeList = ['http']
        if HAVE_SOCKS:
            typeList += ['socks4', 'socks5']

        for item in typeList:
            self._proxyType.addItem(item)

        self._proxyStr = QLineEdit(self)
        self._proxyStr.setPlaceholderText("user:pass@host:port")

        hLayout.addWidget(self._proxyType)
        hLayout.addWidget(self._proxyStr)

        l.addLayout(hLayout)

        self._proxyDns = QCheckBox("Proxy DNS", self)
        l.addWidget(self._proxyDns)

        if not HAVE_SOCKS:
            self._proxyDns.setToolTip("Install pysocks for socks support.")

        self._proxyDns.setEnabled(False)

        self._proxyStr.textChanged.connect(self.__changed)
        self._proxyType.currentIndexChanged.connect(self.__typeChanged)
        self._proxyDns.toggled.connect(self.__dnsChanged)

    def __changed(self):
        self.changed.emit(self.str())

    def __typeChanged(self, index):
        """ From proxy type combobox
        """
        if index == 0:
            self._proxyDns.setEnabled(False)
            self._proxyDns.setToolTip("Not available for http proxy.")
        else:
            self._proxyDns.setEnabled(True)

        if self.str(): self.__changed()

    def __dnsChanged(self, state):
        """ From proxy dns checkbox
        """
        if self.str(): self.__changed()

    def reset(self):
        self._proxyDns.setChecked(True)
        self._proxyStr.setText("")
        self._proxyType.setCurrentIndex(0)
        self.__typeChanged(0)

    def setStr(self, _str):
        self.reset()
        seq = _str.split(':')
        if len(seq) > 1:
            index = self._proxyType.findText(seq[0].rstrip('h'))
            if index != -1:
                self._proxyType.setCurrentIndex(index)
                self._proxyStr.setText(_str[len(seq[0]) + 3:])

            if seq[0] not in ['socks5h', 'socks4h']:
                self._proxyDns.setChecked(False)

    def str(self):
        if self._proxyStr.text():
            return "{0}://{1}".format(
                self.protocolStr(),
                self._proxyStr.text())
        return ""

    def protocol(self): return self._proxyType.currentText()

    def protocolStr(self):
        if (self.protocol() in ['socks4', 'socks5']
                and self._proxyDns.isChecked()):
            return "{0}h".format(self.protocol())

        return self.protocol()


class UrlDialog(QDialog):
    def __init__(self, url='', parent=None):
        QDialog.__init__(self, parent=parent)

        layout = QFormLayout(self)

        label = QLabel("URL:")
        self._urlEdit = QLineEdit(self)
        self._urlEdit.setToolTip(
            "Make sure the URL is valid and has a valid scheme! Valid"
            " schemes are http:// and https://"
        )
        self._urlEdit.setText(url)
        layout.addRow(label, self._urlEdit)

        self._cancelButton = Button("Cancel", self)
        self._saveButton = Button("Save", self)
        layout.addRow(self._cancelButton, self._saveButton)

        self._urlEdit.textChanged.connect(self.__inputChanged)
        self._saveButton.clicked.connect(self.accept)
        self._cancelButton.clicked.connect(self.reject)

    def __inputChanged(self, text):
        if self.isValid():
            self._saveButton.setEnabled(True)
        else:
            self._saveButton.setEnabled(False)

    def isValid(self):
        parsedUrl = urlparse(self._urlEdit.text())
        if parsedUrl.scheme in ['http', 'https'] and parsedUrl.netloc:
            return True
        return False

    @property
    def url(self):
        if self.isValid():
            return self._urlEdit.text()
        return ''


class RequestsSettings(CollapsableGroupBox):
    def __init__(self, model, parent=None):
        CollapsableGroupBox.__init__(self, txt="Connection", parent=parent)

        self._model = model

        l = QFormLayout(self.contentWidget)

        # Verify checkbox
        self._verifyCheck = QCheckBox(self.contentWidget)
        l.addRow(QLabel("Verify (SSL):"), self._verifyCheck)

        # Timeout double spinbox
        self._timeoutSpin = QDoubleSpinBox(self.contentWidget)
        self._timeoutSpin.setSuffix(" sec")
        self._timeoutSpin.setMinimum(3)
        self._timeoutSpin.setMaximum(300)
        l.addRow(QLabel("Timeout:"), self._timeoutSpin)

        # Proxy group box
        self._proxyBox = CollapsableGroupBox(
            txt="Proxy", parent=self.contentWidget)

        l.addRow(QLabel("Proxy:"), self._proxyBox)

        proxyL = QFormLayout(self._proxyBox.contentWidget)

        self._httpProxy = ProxyWidget(self._proxyBox)
        self._httpsProxy = ProxyWidget(self._proxyBox)

        proxyL.addRow(QLabel("Http:"), self._httpProxy)
        proxyL.addRow(QLabel("Https:"), self._httpsProxy)

        self._changed()

        self._timeoutSpin.valueChanged.connect(self.__timeoutEdited)
        self._verifyCheck.stateChanged.connect(self.__verifyEdited)

        self._httpProxy.changed.connect(self.__proxyEdited)
        self._httpsProxy.changed.connect(self.__proxyEdited)

    def __timeoutEdited(self, value):
        self._model.timeout = value

    def __verifyEdited(self, state):
        self._model.verify = bool(state)

    def __proxyEdited(self, text):
        self._model.proxies = {
            'http': self._httpProxy.str(), 'https': self._httpsProxy.str()}

    def _changed(self):
        self._verifyCheck.setChecked(self._model.verify)
        self._timeoutSpin.setValue(self._model.timeout)

        self._httpProxy.setStr(self._model.proxies.get('http', 'socks5h://'))
        self._httpsProxy.setStr(self._model.proxies.get('https', 'socks5h://'))


class Stats2Settings(CollapsableGroupBox):
    def __init__(self, model, parent=None):
        """
        @type model: SearxStats2Model
        """
        CollapsableGroupBox.__init__(self, txt="searx-stats2", parent=parent)

        self._model = model

        layout = QVBoxLayout(self.contentWidget)

        infoLabel = QLabel(
            "The searx-stats2 project lists public searx instances with"
            " statistics. The original instance is running at"
            " https://searx.space/. This is where searx-qt will request"
            " a list with instances when the update button is pressed."
            , self
        )
        infoLabel.setWordWrap(True)

        layout.addWidget(infoLabel)

        hLayout = QHBoxLayout(self.contentWidget)

        label = QLabel("URL:", self)
        label.setSizePolicy(
            QSizePolicy(
                QSizePolicy.Maximum, QSizePolicy.Maximum
            )
        )
        self._urlLabel = QLabel(model.url, self)
        self._urlEditButton = Button("Edit", self)
        self._urlResetButton = Button("Reset", self)

        hLayout.addWidget(label)
        hLayout.addWidget(self._urlLabel)
        hLayout.addWidget(self._urlEditButton)
        hLayout.addWidget(self._urlResetButton)

        layout.addLayout(hLayout)

        self._urlEditButton.clicked.connect(self.__urlEditClicked)
        self._urlResetButton.clicked.connect(self.__urlResetClicked)
        model.changed.connect(self.__modelChanged)

    def __modelChanged(self):
        self._urlLabel.setText(self._model.url)

    def __urlEditClicked(self):
        dialog = UrlDialog(self._model.url)
        if dialog.exec():
            self._model.url = dialog.url

    def __urlResetClicked(self):
        self._model.reset()


class SettingsWindow(QFrame):
    def __init__(self, model, parent=None):
        """
        @type model: SettingsModel
        """
        QFrame.__init__(self, parent=parent)
        layout = QVBoxLayout(self)
        self.setWindowTitle("Settings")

        self._requestsView = RequestsSettings(model.requests, self)
        layout.addWidget(self._requestsView, 0, Qt.AlignTop)

        self._stats2View = Stats2Settings(model.stats2, self)
        layout.addWidget(self._stats2View, 0, Qt.AlignTop)

        # keep widgets aligned on top
        spacer = QSpacerItem(
            20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding
        )
        layout.addItem(spacer)

        self.resize(480, 400)
